Skip to content

Generalize mode-overlap detectors and S-parameter calculation to custom reference modes - #405

Merged
ymahlau merged 3 commits into
ymahlau:mainfrom
bruxillensis:pr1-mode-overlap-detectors
Jul 29, 2026
Merged

Generalize mode-overlap detectors and S-parameter calculation to custom reference modes#405
ymahlau merged 3 commits into
ymahlau:mainfrom
bruxillensis:pr1-mode-overlap-detectors

Conversation

@bruxillensis

@bruxillensis bruxillensis commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

ModeOverlapDetector currently only supports reference modes produced by the tidy3d waveguide mode solver. My motivation was that I wanted to use ModeOverlapDetector for gaussians and custom (user defined or solved for) modes, for example with inverse design of a grating into a radiating mode. This PR splits the overlap machinery into an abstract base class and adds two new detectors that overlap the simulated fields against user-supplied or analytic reference modes — enabling coupling-efficiency and S-parameter workflows for free-space beams or modes imported from other tools, without involving the mode solver.

What changed

  • BaseModeOverlapDetector (new ABC): owns everything independent of how the reference mode is produced — the stored mode fields, detector-plane face-area weights, the dispersion/conductivity correction (the reference mode sees the full complex ε(ω_c), same as before), and the overlap integral (compute_overlap / compute_overlap_to_mode). Subclasses implement a single hook, _compute_mode_fields, called once per wave character.
  • ModeOverlapDetector: unchanged behavior, now a subclass that implements the hook via compute_mode (tidy3d).
  • CustomModeOverlapDetector (new): takes a mode_function callable, invoked per frequency with the detector-plane coordinate meshgrids, the frequency, the propagation axis, and the effective inverse permittivity on the plane. Returns (mode_E, mode_H) in FDTDX's η₀-normalized convention; optional renormalization to unit Poynting flux (matching the mode solver).
  • GaussianModeOverlapDetector (new) + gaussian_mode_function: ready-made analytic Gaussian reference mode. Polarization and propagation tilt are configured exactly like GaussianPlaneSource (polarization axis or explicit vectors, azimuth_angle/elevation_angle in degrees), with the medium index derived from the permittivity slice — dispersion-aware via the shared base-class correction. Under a tilt, a transverse phase ramp matches the tilted wavefront; at normal incidence the mode stays real.
  • S-parameters: calculate_sparam / the sparams utilities accept any BaseModeOverlapDetector, so S-parameters can be computed against custom reference modes.
  • Shared polarization helper: the tilted E/H/k derivation used by the plane sources is factored into core.misc.tilted_polarization_vectors and reused by the analytic Gaussian mode, so source and detector stay on one convention by construction.

Usage

  det = fdtdx.GaussianModeOverlapDetector(
      name="fiber_coupling",
      partial_grid_shape=(None, None, 1),
      wave_characters=(fdtdx.WaveCharacter(wavelength=1.55e-6),),
      mode_radius=2.0e-6,
      direction="+",
  )

  # or an arbitrary reference mode:
  det = fdtdx.CustomModeOverlapDetector(
      name="custom",
      partial_grid_shape=(None, None, 1),
      wave_characters=(wave,),
      mode_function=my_mode,  # (coordinates, frequency, propagation_axis, inv_permittivity) -> (E, H)
  )

No behavior change for existing ModeOverlapDetector users; the refactor is purely structural and all public APIs are preserved.

Summary by CodeRabbit

  • New Features

    • Added custom and analytic Gaussian mode-overlap detectors.
    • Added support for tilted, polarized, and diverging Gaussian reference modes.
    • Exposed new mode-detector tools and helpers through the top-level API.
    • Added support for custom mode functions and optional flux normalization.
    • Extended S-parameter calculations to support all mode-overlap detector types.
  • Documentation

    • Added the new mode-overlap detectors and Gaussian helpers to the API reference.
  • Bug Fixes

    • Improved polarization and wave-vector handling for angled plane-wave sources.
  • Tests

    • Added comprehensive validation for Gaussian modes, custom modes, dispersion, tilts, divergence, and detector overlap behavior.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Mode-overlap detection is refactored around a shared base class, with custom and analytic Gaussian reference modes, tilted polarization handling, generalized S-parameter support, public exports, documentation, and unit/physics validation.

Changes

Mode overlap detector extensions

Layer / File(s) Summary
Analytic modes and shared detector base
src/fdtdx/objects/detectors/mode.py
Adds Gaussian mode builders and a shared abstract detector workflow for plane coordinates, material reduction, per-frequency reference fields, and overlap state.
Concrete detector variants
src/fdtdx/objects/detectors/mode.py
Refactors waveguide detection and adds custom callable and Gaussian detectors with validation and optional Poynting-flux normalization.
Tilted polarization resolution
src/fdtdx/core/misc.py, src/fdtdx/objects/sources/linear_polarization.py
Adds a shared tilted polarization/vector resolver and uses it in linearly polarized plane sources.
S-parameter and public API integration
src/fdtdx/utils/sparams.py, src/fdtdx/__init__.py, docs/source/07_api.rst
Generalizes S-parameter detector checks and exports and documents the new detector classes and Gaussian helper.
Detector and Gaussian physics validation
tests/unit/objects/detectors/test_mode_custom.py, tests/simulation/physics/detectors/test_mode_overlap.py
Adds coverage for detector contracts, dispersion, polarization, tilt, divergence, direction selectivity, normalization, and analytic overlap predictions.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Simulation
  participant GaussianPlaneSource
  participant GaussianModeOverlapDetector
  participant ComputeOverlap
  Simulation->>GaussianPlaneSource: generate Gaussian beam fields
  Simulation->>GaussianModeOverlapDetector: apply detector-plane data
  GaussianModeOverlapDetector-->>Simulation: store analytic reference modes
  Simulation->>ComputeOverlap: compute modal overlap
  ComputeOverlap-->>Simulation: return overlap power
Loading

Possibly related PRs

  • ymahlau/fdtdx#312: Updates related nonuniform-grid mode-overlap coordinate and integration behavior.
  • ymahlau/fdtdx#353: Changes configuration and solver forwarding in the same mode detector implementation.
  • ymahlau/fdtdx#354: Introduces axis utilities used by the tilted polarization changes.

Poem

A rabbit bounds through modes of light,
Gaussian beams take graceful flight.
Tilted fields now dance and glow,
Custom paths know where to go.
Detectors measure, tests cheer bright—
Hop, hop, overlap done right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: generalized mode-overlap detectors and S-parameter handling for custom reference modes.
Docstring Coverage ✅ Passed Docstring coverage is 85.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.97701% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.52%. Comparing base (37f4039) to head (823e356).

Files with missing lines Patch % Lines
src/fdtdx/objects/detectors/mode.py 95.51% 4 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #405      +/-   ##
==========================================
+ Coverage   90.35%   90.52%   +0.17%     
==========================================
  Files          92       92              
  Lines       11789    11920     +131     
  Branches     1803     1820      +17     
==========================================
+ Hits        10652    10791     +139     
+ Misses        794      783      -11     
- Partials      343      346       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ymahlau

ymahlau commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Hi @bruxillensis, thanks for another PR! I skimmed over the code and the structural changes, which all look/sound good to me. If you want to mark the PR as ready for review I am happy to give a more detailed look at the code.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/fdtdx/objects/detectors/mode.py`:
- Around line 105-115: Update the Gaussian envelope calculation in the tilted
branch of the detector reference-field implementation to use the coordinate
displacement projected orthogonally to wave_vector, matching
GaussianPlaneSource’s beam-plane footprint; retain the existing detector-plane
calculation for untilted waves and add a regression comparing the tilted
detector profile against the source projection.
- Around line 148-164: Update both analytic Gaussian-mode paths, including the
flow around gaussian_mode_fields, to retain and propagate inv_permeability_slice
through the helper/factory instead of discarding it. Compute wavenumber using
the effective permittivity and permeability product, and scale the
magnetic/electric field relationship using their ratio. Add coverage for a
non-unit-permeability medium and verify the generated mode and overlap
normalization.
- Around line 25-40: Validate radius at the start of gaussian_mode_fields and
reject zero or negative values before computing the Gaussian exponent,
normalization, or overlaps. Raise the established argument-validation exception
with a clear message, while preserving behavior for positive radii.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c14361c5-c9fa-48df-858e-ca3ceba7ebf0

📥 Commits

Reviewing files that changed from the base of the PR and between 9360cc3 and d330666.

📒 Files selected for processing (7)
  • docs/source/07_api.rst
  • src/fdtdx/__init__.py
  • src/fdtdx/core/misc.py
  • src/fdtdx/objects/detectors/mode.py
  • src/fdtdx/objects/sources/linear_polarization.py
  • src/fdtdx/utils/sparams.py
  • tests/unit/objects/detectors/test_mode_custom.py

Comment thread src/fdtdx/objects/detectors/mode.py
Comment thread src/fdtdx/objects/detectors/mode.py
Comment thread src/fdtdx/objects/detectors/mode.py
@bruxillensis
bruxillensis marked this pull request as ready for review July 13, 2026 09:48
@bruxillensis

Copy link
Copy Markdown
Contributor Author

Marked! Its exciting to see the contributor growth, you have many PR's to review now!

Comment thread src/fdtdx/objects/detectors/mode.py Outdated

@autoinit
class GaussianModeOverlapDetector(BaseModeOverlapDetector):
"""Mode-overlap detector using an analytic Gaussian beam as the reference mode.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would maybe say "Tilted Gaussian" here not "Gaussian Beam" to avoid confusion with focused Gaussian beam, or generally disambiguate this difference a bit in the docstrings

@ymahlau

ymahlau commented Jul 14, 2026

Copy link
Copy Markdown
Owner

I gave the code a closer look, one more comment in addition to the ones above:

  • I think the test cases could be more extensive: A simulation test measuring overlap between Gaussian Source and OverlapDetector would validate the correct functionality

Other than that, the code looks good to me!

@bruxillensis
bruxillensis force-pushed the pr1-mode-overlap-detectors branch from d330666 to 823e356 Compare July 27, 2026 21:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/unit/objects/detectors/test_mode_custom.py (2)

356-360: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate _plane_coords() helper.

TestGaussianModeGeneralization._plane_coords (356-360) and TestGaussianRadiusValidation._plane_coords (463-467) are byte-for-byte identical. Consider hoisting to a module-level function/fixture to avoid the two copies drifting apart later.

Also applies to: 463-467

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/objects/detectors/test_mode_custom.py` around lines 356 - 360,
Remove the duplicate _plane_coords methods from TestGaussianModeGeneralization
and TestGaussianRadiusValidation, define one shared module-level helper or
fixture with the same meshgrid behavior, and update both test classes to use it.

236-245: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale docstring on mk().

mk's docstring says "Build a placed detector for the given mode function," but it actually broadcasts a per-pole ADE coefficient scalar into a (1, 1, 8, 8, 8) array — a leftover/copy-paste artifact that misdescribes the helper.

📝 Proposed fix
         def mk(a):  # broadcast a per-pole scalar to (num_poles, 1, Nx, Ny, Nz)
-            """Build a placed detector for the given mode function."""
+            """Broadcast a per-pole coefficient to the dispersion-array shape."""
             return jnp.full((1, 1, 8, 8, 8), float(a[0]), dtype=jnp.float32)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/objects/detectors/test_mode_custom.py` around lines 236 - 245,
Update the nested helper mk inside _dispersive_coeffs so its docstring describes
broadcasting a per-pole ADE coefficient scalar into the detector-shaped (1, 1,
8, 8, 8) array; remove the stale reference to building a placed detector from a
mode function.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unit/objects/detectors/test_mode_custom.py`:
- Around line 356-360: Remove the duplicate _plane_coords methods from
TestGaussianModeGeneralization and TestGaussianRadiusValidation, define one
shared module-level helper or fixture with the same meshgrid behavior, and
update both test classes to use it.
- Around line 236-245: Update the nested helper mk inside _dispersive_coeffs so
its docstring describes broadcasting a per-pole ADE coefficient scalar into the
detector-shaped (1, 1, 8, 8, 8) array; remove the stale reference to building a
placed detector from a mode function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0acd3ca5-2ec2-4c1f-85f8-a3d0090d8ac6

📥 Commits

Reviewing files that changed from the base of the PR and between d330666 and 823e356.

📒 Files selected for processing (9)
  • docs/source/07_api.rst
  • src/fdtdx/__init__.py
  • src/fdtdx/core/misc.py
  • src/fdtdx/objects/detectors/mode.py
  • src/fdtdx/objects/sources/linear_polarization.py
  • src/fdtdx/utils/sparams.py
  • tests/simulation/physics/detectors/__init__.py
  • tests/simulation/physics/detectors/test_mode_overlap.py
  • tests/unit/objects/detectors/test_mode_custom.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • docs/source/07_api.rst
  • src/fdtdx/init.py
  • src/fdtdx/utils/sparams.py
  • src/fdtdx/objects/sources/linear_polarization.py
  • src/fdtdx/core/misc.py
  • src/fdtdx/objects/detectors/mode.py

@bruxillensis

Copy link
Copy Markdown
Contributor Author

@ymahlau I think this is ready for another look. Was on a work trip for the past few weeks. Comments were addressed, and I added divergence_angle since the original implementation assumed a perfectly collimated beam. It feels slightly unintuitive to me still to construct a gaussian detector (as in, you need to understand how the parameters are used), but maybe thats unavoidable. Open to feedback on it.

@ymahlau

ymahlau commented Jul 29, 2026

Copy link
Copy Markdown
Owner

I also can't think of a better solution from the top of my head, but I think it is also okay to expect a little understanding of what the parameters do. Thanks for the PR!

@ymahlau
ymahlau merged commit d6e22c6 into ymahlau:main Jul 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants